package com.apestech.framework.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 日期时间相关工具合集，支持国际化，时区访问调用，默认日期格式为：yyyy-MM-dd HH:mm:ss <br>
 * 支持常规各区域默认时间格式，自动根据字串获取对应时间，如果对应时间返回为null,说明入参时间为非法时间格式  <br>
 * dateToStr为时间转字串， legalDate为字串转时间  <br>
 * strToTimezone 字串类型时间为入参，获取格式化之后某一时区下的时间字串 <br>
 * getTimeDate 时间类型变量为入参，获取某一时区下的时间 <br>
 * addByDate 根据某一时间，加减天之后返回的时间
 * addByMinute 根据某一时间，加减分钟之后返回的时间
 * addByYear 根据某一时间，加减年之后返回的时间
 * 
 * yyyy年MM月dd日 hh:mm:ss SSS a
 * @author hupo
 *
 */
public final class DateUtil extends StringUtil {
	
	public static String DEFLAUT_DATE="yyyy-MM-dd";
	public static String DEFLAUT_DATETIME="yyyy-MM-dd HH:mm:ss";
	public static String DEFLAUT_DATETIME_HM="yyyy-MM-dd HH:mm:ss SSS";
	public static String DEFLAUT_DATETIME_HM_1="yyyy-MM-dd'T'HH:mm:ss.S'Z'";
  
    /**  
     * 对日期(时间)中的日进行加减计算. <br>  
     * 如果Date类型的d为 2005年8月20日,那么 <br>  
     * amount=-10的值为2005年8月10日 <br>  
     * amount=10的值为2005年8月30日 <br>  
     *   
     * @param d 日期(时间).  
     * @param amount 加减计算的幅度.+n=加n天;-n=减n天.  
     * @return 计算后的日期(时间).  
     */
    public static Date addByDate(Date d, int amount) {   
        return addDate(d, GregorianCalendar.DATE, amount);   
    }   
       
    /**  
     * 对日期(时间)中的分进行加减计算.  
     *   
     * @param d 日期(时间).  
     * @param amount 加减计算的幅度.+n=加n分;-n=减n分.  
     * @return 计算后的日期(时间).  
     */
    public static Date addByMinute(Date d, int amount) {   
        return addDate(d, GregorianCalendar.MINUTE, amount);   
    }   
       
    /**  
     * 对日期(时间)中的年进行加减计算.  
     *   
     * @param d 日期(时间).  
     * @param amount 加减计算的幅度.+n=加n年;-n=减n年.  
     * @return 计算后的日期(时间).  
     */
    public static Date addByYear(Date d, int amount) {   
        return addDate(d, GregorianCalendar.YEAR, amount);   
    }

	/**
	 * 对日期(时间)中的月进行加减计算.
	 * @param d
	 * @param amount
	 * @return
	 */
	public static Date addByMonth(Date d, int amount) {
		return addDate(d, GregorianCalendar.MONTH, amount);
	}

    /**
	 * 对日期(时间)中的日进行加减计算.
	 * @param d
	 * @param amount
	 * @return
	 */
	public static Date addByDay(Date d, int amount) {
		return addDate(d, GregorianCalendar.DATE, amount);
	}

	/**
     * 对日期(时间)中由field参数指定的日期成员进行加减计算. <br>  
     * 如果Date类型的d为 2005年8月20日,那么 <br>  
     * calculate(d,GregorianCalendar.YEAR,-10)的值为1995年8月20日 <br>  
     * 而calculate(d,GregorianCalendar.YEAR,+10)的值为2015年8月20日 <br>  
     *   
     * @param d 日期(时间).  
     * @param field 日期成员主要有: <br>  
     *            年:GregorianCalendar.YEAR <br>  
     *            月:GregorianCalendar.MONTH <br>  
     *            日:GregorianCalendar.DATE <br>  
     *            时:GregorianCalendar.HOUR <br>  
     *            分:GregorianCalendar.MINUTE <br>  
     *            秒:GregorianCalendar.SECOND <br>  
     *            毫秒:GregorianCalendar.MILLISECOND <br>  
     * @param amount 加减计算的幅度.+n=加n个由参数field指定的日期成员值;-n=减n个由参数field代表的日期成员值.  
     * @return 计算后的日期(时间).  
     */  
    private static Date addDate(Date d, int field, int amount) {   
        if (d == null)   
            return null;   
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(d);
		calendar.add(field, amount);
		return calendar.getTime();
    }

    /**
     * 获取当前日期对应的星期数.  
     * <br>1=星期天,2=星期一,3=星期二,4=星期三,5=星期四,6=星期五,7=星期六  
     * 
     * @param d java.util.Date类的实例.  
     * @return 当前日期对应的星期数  
     */  
    public static int dayOfWeek(Date d) {   
        GregorianCalendar g = new GregorianCalendar();
        g.setTime(d);
        int ret = g.get(java.util.Calendar.DAY_OF_WEEK);   
        g = null;   
        return ret;   
    }   
  
    /**  
     * 获取所有的时区编号. <br>  
     * 排序规则:按照ASCII字符的正序进行排序. <br>  
     * 排序时候忽略字符大小写.  
     *   
     * @return 所有的时区编号(时区编号已经按照字符[忽略大小写]排序).  
     */  
    public static String[] fecthAllTimeZoneIds() {
    	return getOrderList(TimeZone.getAvailableIDs());   
    } 
    
    /**
     * 根据某一时间，对应时区ID，获取对应时区的时间
     * @param date
     * @param timeZoneId
     * @return
     */
    public static Date getTimeDate(Date date, String timeZoneId){
    	int diffTime = getDiffTimeZoneRawOffset(timeZoneId);
        return getTimeDate(date, diffTime);
    }
    
    public static Date getTimeDate(Date date, int diffTime){
    	long nowTime = date.getTime();   
        long newNowTime = nowTime - diffTime;   
        date = new Date(newNowTime);   
        return date;
    }
    
    /**
     * 获取带时间差的格林威尔时间
     * @param date
     * @param timezoneOffset
     * @return
     */
    public static Date getGreenvilleTimeDate(Date date, int timezoneOffset){
    	int d=(timezoneOffset + 480) * 60 * 1000;
    	return getTimeDate(date, d);
    }
    
    /**  
     * 将日期时间字符串根据转换为指定时区的日期时间.
     * @param srcDateTime 待转化的日期时间.  
     * @param dstFormater 目标的日期时间的格式.  
     * @param dstTimeZoneId 目标的时区编号.  
     *   
     * @return 转化后的日期时间.  
     * @throws Exception 
     */  
    public static String strToTimezone(String srcDateTime, String dstFormater, String dstTimeZoneId) throws Exception {
    	Date srcDate=strToDate(srcDateTime);
    	if(srcDate==null){
    		throw new Exception(getMessage("DateUtil_errorformatDate","错误的日期格式({0})！",srcDateTime));
    	}
    	Date dstDate=getTimeDate(srcDate, dstTimeZoneId);
    	return dateToStr(dstDate, dstFormater);
    }   
    
    /**
     * 获取时区时间
     * @param srcDateTime
     * @param l
     * @param dstTimeZoneId
     * @return
     * @throws Exception 
     */
    public static String strToTimezone(String srcDateTime, Locale l, String dstTimeZoneId) throws Exception {
    	Date srcDate=strToDate(srcDateTime);
    	if(srcDate==null){
    		throw new Exception(getMessage("DateUtil_errorformatDate","错误的日期格式({0})！",srcDateTime));
    	}
    	Date dstDate=getTimeDate(srcDate, dstTimeZoneId);
    	return dateToStr(dstDate, l);
    }
    	
    /**  
     * 获取系统当前默认时区与指定时区的时间差.(单位:毫秒)  
     * @param timeZoneId 时区Id  
     * @return 系统当前默认时区与指定时区的时间差.(单位:毫秒)  
     */  
    private static int getDiffTimeZoneRawOffset(String timeZoneId) {   
        return TimeZone.getDefault().getRawOffset() - TimeZone.getTimeZone(timeZoneId).getRawOffset();   
    }
    
    /**
     * 根据常规默认日期格式获取字串类型日期
     * @param d
     * @return
     */
    public static String dateToStr(Date d) {   
    	return dateToStr(d, DEFLAUT_DATE);   
    }  
    
    /**
     * 根据常规默认日期格式获取字串类型日期时间
     * @param d
     * @return
     */
    public static String dateTimeToStr(Date d) {
    	return dateToStr(d, DEFLAUT_DATETIME);   
    }
  
    /**  
     * 日期(时间)转化为字符串.  
     *   
     * @param formater 日期或时间的格式.  
     * @param d java.util.Date类的实例.  
     * @return 日期转化后的字符串.  
     */  
    public static String dateToStr(Date d, String formater) {   
        if (formater == null || "".equals(formater))   
            return null;   
        if (d == null) return null;   
        return (new SimpleDateFormat(formater)).format(d);   
    }
    
    /**
     * 根据地区方式显示的时间格式
     * @param d
     * @param l
     * @return
     */
    public static String dateToStr(Date d, Locale l){
    	return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, l).format(d);
    }    
    
	/**
	 * 根据字串类型时间，获取时间格式，如果不存在，将直接返回null，调用位置可补上“非法日期格式提示”
	 * @param date
	 * @return
	 */
	public static Date strToDate(String date){
		Date datetime=null;
		if("".equals(date)){
			return null;
		}
		if(date.indexOf(":")<0) date=date+" 00:00:00";
		datetime=getDate(date);
		if(datetime==null){
			if(date.indexOf(":")<0){
				datetime=legalDefDateTime(date, DEFLAUT_DATE);
				if(datetime==null){
					datetime=legalLocaleDate(date);
				}
			} else {
				datetime=legalDefDateTime(date, DEFLAUT_DATETIME);
				if(datetime==null){
					datetime=legalLocaleDateTime(date);
					if(datetime==null){
						datetime=legalDefDateTime(date, DEFLAUT_DATETIME_HM);
						if(datetime==null){
							datetime=legalDefDateTime(date, DEFLAUT_DATETIME_HM_1);
						}
					}
				}
			}
		}
		return datetime;
	}
	
	/**
	 * 获取日期
	 * @param date
	 * @return
	 */
	private static Date getDate(String date){
    	try {
			long time=Date.parse(date);
			return new Date(time);
		} catch (Exception e) {
			return null;
		}
    }
	
	/**
	 * 根据日期时间字串获取合法的区域
	 * @param date
	 * @return
	 */
	public static Date legalDefDateTime(String date, String pattern){
		try{
			return (new SimpleDateFormat(pattern)).parse(date);
		} catch (ParseException e1) {
		}
		return null;
	}
	
	/**
	 * 根据日期时间字串取合法的区域
	 * @param date
	 * @return
	 */
	public static Date legalLocaleDateTime(String date){
		Locale[] locales = Locale.getAvailableLocales();
		DateFormat df = null;
		Date datetime=null;
		for(Locale locale : locales){
			if(!"".equals(locale.getCountry())){
				datetime=legalDateTime(date, DateFormat.SHORT, locale);
				if(datetime==null){
					datetime=legalDateTime(date, DateFormat.MEDIUM, locale);
					if(datetime!=null){
						datetime=legalDateTime(date, DateFormat.FULL, locale);
						if(datetime!=null){
							return datetime;
						}
					}
				} else {
					return datetime;
				}
			}
		}
		return null;
	}
	
	/**
	 * 根据日期字串获取合法的区域
	 * @param date
	 * @return
	 */
	public static Date legalLocaleDate(String date){
		Locale[] locales = Locale.getAvailableLocales();
		DateFormat df = null;
		Date datetime=null;
		for(Locale locale : locales){
			if(!"".equals(locale.getCountry())){
				datetime=strToDate(date, DateFormat.SHORT, locale);
				if(datetime==null){
					datetime=strToDate(date, DateFormat.MEDIUM, locale);
					if(datetime!=null){
						datetime=strToDate(date, DateFormat.FULL, locale);
						if(datetime!=null){
							return datetime;
						}
					}
				} else {
					return datetime;
				}
			}
		}
		return null;
	}
	
	/**
	 * 根据日期时间字串取合法的区域
	 * @param date
	 * @return
	 */
	public static Locale legalLocaleDateTimeLocale(String date){
		Locale[] locales = Locale.getAvailableLocales();
		DateFormat df = null;
		for(Locale locale : locales){
			if(!"".equals(locale.getCountry())){
				if(legalDateTime(date, DateFormat.SHORT, locale)==null){
					if(legalDateTime(date, DateFormat.MEDIUM, locale)!=null){
						return locale;
					}
				} else {
					return locale;
				}
			}
		}
		return null;
	}
	
	/**
	 * 根据日期字串获取合法的区域
	 * @param date
	 * @return
	 */
	public static Locale legalLocaleDateLocale(String date){
		Locale[] locales = Locale.getAvailableLocales();
		DateFormat df = null;
		for(Locale locale : locales){
			if(!"".equals(locale.getCountry())){
				if(strToDate(date, DateFormat.SHORT, locale)==null){
					if(strToDate(date, DateFormat.MEDIUM, locale)!=null){
						return locale;
					}
				} else {
					return locale;
				}
			}
		}
		return null;
	}
	
	/**
	 * 判断是否为区域合法日期时间
	 * @param date
	 * @param lx
	 * @param locale
	 * @return
	 */
	private static Date legalDateTime(String date, int lx, Locale locale){
		try{
			return DateFormat.getDateTimeInstance(lx,lx,locale).parse(date);
		} catch (ParseException e1) {
		}
		return null;
	}
	
	/**
	 * 判断是否为区域合法日期
	 * @param date
	 * @param lx
	 * @param locale
	 * @return
	 */
	private static Date strToDate(String date, int lx, Locale locale){
		try{
			return DateFormat.getDateInstance(lx,locale).parse(date);
		} catch (ParseException e1) {
		}
		return null;
	}

	/**
	 * 根据DATE类型返回时间戳类型字串
	 * @param date
	 * @return
	 */
	public static String getTimeStampStr(Date date){
		Calendar calendar2 = Calendar.getInstance();
		calendar2.setTimeInMillis(date.getTime());
		int year = calendar2.get(Calendar.YEAR);
		int month = calendar2.get(Calendar.MONTH);
		int day = calendar2.get(Calendar.DAY_OF_MONTH);
		int hour = calendar2.get(Calendar.HOUR_OF_DAY);//24小时制
//		int hour = calendar2.get(Calendar.HOUR);//12小时制
		int minute = calendar2.get(Calendar.MINUTE);
		int second = calendar2.get(Calendar.SECOND);
		int minsecond = calendar2.get(Calendar.MILLISECOND);
		String text= prefixStr(castToString(year), "0", 4) +
				prefixStr(castToString(month + 1), "0", 2)+
				prefixStr(castToString(day), "0", 2)+"."+
				prefixStr(castToString(hour), "0", 2)+
				prefixStr(castToString(minute), "0", 2)+
				prefixStr(castToString(second), "0", 2)+"."+
				prefixStr(castToString(minsecond), "0", 3);
		return text;
	}

	public static String getStartDateStr(Date date, int amount){
		return dateToStr(DateUtil.addByDate(date, amount))+" 00:00:00";
	}

	public static String getEndDateStr(Date date, int amount){
		return dateToStr(DateUtil.addByDate(date, amount))+" 23:59:59";
	}

	public static String getStartDate(String date){
		if(date==null) return null;
		date=date.trim();
		if(date.length()==24) date=leftStr(date,10);
		int pos=date.indexOf(" ");
		if(pos>0){
			return date.substring(0, pos)+" 00:00:00";
		}
		return date+" 00:00:00";
	}

	public static String getEndDate(String date){
		if(date==null) return null;
		date=date.trim();
		if(date.length()==24) date=leftStr(date,10);
		int pos=date.indexOf(" ");
		if(pos>0){
			return date.substring(0, pos)+" 23:59:59";
		}
		return date+" 23:59:59";
	}
	
}
